#pragma once

#include <vex/iterable/range_base.h>

namespace tests {

    template<class TRef>
    struct range_archetype_value_holder {
        typedef typename std::decay<TRef>::type holder_value_type;
        static holder_value_type s_value;
    };

    template<>
    struct range_archetype_value_holder<void> {};

    template< class TRef >
    typename range_archetype_value_holder<TRef>::holder_value_type range_archetype_value_holder<TRef>::s_value;

    template< class TRangeCategory = vex::iterable::trivial_range_tag, class TRef = void >
    struct range_archetype 
        : public vex::iterable::range_base<vex::iterable::trivial_range_tag, void>
        , public range_archetype_value_holder<void>
    {
        typedef range_archetype this_type;

        bool move_front()
        {
            return false;
        }
    };

    template< class TRef >
    struct range_archetype<vex::iterable::output_range_tag, TRef> 
        : public vex::iterable::range_base<vex::iterable::output_range_tag, TRef> 
        , public range_archetype_value_holder<TRef>
    {
        typedef range_archetype this_type;

        bool move_front()
        {
            return false;
        }

        bool put(typename TRef const &)
        {
            return false;
        }
    };

    template< class TRef >
    struct range_archetype<vex::iterable::one_pass_range_tag, TRef> 
        : public vex::iterable::range_base<vex::iterable::one_pass_range_tag, TRef> 
        , public range_archetype_value_holder<TRef>
    {
        typedef range_archetype this_type;

        bool move_front()
        {
            return false;
        }

        TRef front() const
        {
            return s_value;
        }
    };

    template< class TRef >
    struct range_archetype<vex::iterable::forward_range_tag, TRef> 
        : public vex::iterable::range_base<vex::iterable::forward_range_tag, TRef> 
        , public range_archetype_value_holder<TRef>
    {
        typedef range_archetype this_type;

        bool move_front()
        {
            return false;
        }

        TRef front() const
        {
            
            return s_value;
        }

        this_type save() const
        {
            return this_type(*this);
        }
    };

    template< class TRef >
    struct range_archetype<vex::iterable::bidirectional_range_tag, TRef> 
        : public vex::iterable::range_base<vex::iterable::bidirectional_range_tag, TRef> 
        , public range_archetype_value_holder<TRef>
    {
        typedef range_archetype this_type;

        bool move_front()
        {
            return false;
        }

        TRef front() const
        {
            
            return s_value;
        }

        this_type save() const
        {
            return this_type(*this);
        }

        bool move_back()
        {
            return false;
        }

        TRef back() const
        {
            
            return s_value;
        }
    };

    template< class TRef >
    struct range_archetype<vex::iterable::random_access_range_tag, TRef> 
        : public vex::iterable::range_base<vex::iterable::random_access_range_tag, TRef> 
        , public range_archetype_value_holder<TRef>
    {
        typedef range_archetype this_type;

        bool move_front()
        {
            return false;
        }

        TRef front() const
        {
            
            return s_value;
        }

        this_type save() const
        {
            return this_type(*this);
        }

        bool move_back()
        {
            return false;
        }

        TRef back() const
        {
            
            return s_value;
        }

        bool move_front_of(std::size_t)
        {
            return false;
        }

        bool move_back_of(std::size_t)
        {
            return false;
        }

        TRef at(std::size_t) const
        {
            
            return s_value;
        }

        std::size_t size() const
        {
            return 0u;
        }
    };
}
